use serde::Deserialize;
use serde::Serialize;
use serde_json::{Value, json}; 

use super::my_cache;
use super::my_err::MyErr;
use super::util_string;

#[derive(Deserialize)]
pub struct Baidu {
    pub app_id: i64,
    pub api_key: String,
    pub secret_key: String,
}

// 获取用户访问令牌
const GET_TOKEN_URL: &str = "https://aip.baidubce.com/oauth/2.0/token";
// 二维码识别
const GET_OCR_QRCODE_URL: &str = "https://aip.baidubce.com/rest/2.0/ocr/v1/qrcode";

impl Baidu {
    /// 解析url图片上的二维码
    pub async fn parse_qrcode(&self, img_url: &str) -> Result<String, MyErr> {
        let access_token = self.get_access_token().await?;

        let form_data = json!({
            "url" : img_url,
            "location" : false,
        });

        let url = format!("{}?access_token={}", GET_OCR_QRCODE_URL, access_token);
        println!("访问地址:{}", url);
        let rs = post_from_data_to_url(&url, &form_data).await;
        match rs {
            Ok(res) => {
                println!("{:?}", res);
                let codes_result_num = res.get("codes_result_num").and_then(Value::as_i64).unwrap_or_default();
                if codes_result_num > 0 {
                    if let Some(codes_result) = res.get("codes_result").and_then(Value::as_array) {
                        if let Some(code_one) = codes_result.get(0) {
                            if let Some(texts) = code_one.get("text").and_then(Value::as_array) {
                                if let Some(text) = texts.get(0).and_then(Value::as_str) {
                                    Ok(text.to_string())
                                } else {
                                    Err(MyErr::Msg(format!("解析二维码失败！内容:{:?}", res)))
                                }
                            } else {
                                Err(MyErr::Msg(format!("解析二维码失败！内容:{:?}", res)))
                            }
                        } else {
                            Err(MyErr::Msg(format!("解析二维码失败！内容:{:?}", res)))
                        }
                    } else {
                        Err(MyErr::Msg(format!("解析二维码失败！内容:{:?}", res)))
                    }
                } else {
                    let error_code = res.get("error_code").and_then(Value::as_i64).unwrap_or_default();
                    let error_msg = res.get("error_msg").and_then(Value::as_str).unwrap_or_default();
                    Err(MyErr::Msg(format!("解析二维码失败！错误代码:{}, 错误内容:{}", error_code, error_msg)))
                }
            }
            Err(e) => Err(MyErr::Msg(format!("解析二维码失败! 错误：{:?}", e))),
        }
    }

    /// 获取访问令牌（access_token）
    async fn get_access_token(&self) -> Result<String, MyErr> {
        let bd_at_key = format!("{}_BD_AT", self.app_id);
        if let Some(s) = my_cache::get(&bd_at_key) {
            // 如果在缓存中已经存在 jsession 则直接返回
            log::info!("缓存中已经存在 jsession ，直接获取并返回: {}", s);
            Ok(s)
        } else {
            // 如果缓存中没有，则登录接口获取

            let form_data = json!({
                "grant_type" : "client_credentials",
                "client_id":self.api_key,
                "client_secret" : self.secret_key,
            });

            let rs = post_from_data_to_url(&GET_TOKEN_URL, &form_data).await;
            match rs {
                Ok(res) => {
                    if let Some(access_token) = res.get("access_token") {
                        let access_token = access_token.as_str().unwrap_or_default();
                        let expires_in = res.get("expires_in").and_then(Value::as_i64).unwrap_or_default();

                        if !util_string::is_empty(access_token) {
                            my_cache::set(&bd_at_key, access_token.to_string(), expires_in);

                            Ok(access_token.to_string())
                        } else {
                            Err(MyErr::Msg(format!(
                                "获取access_token成功，但内容异常：{} url：{}",
                                access_token, GET_TOKEN_URL
                            )))
                        }
                    } else {
                        let error = res.get("error").and_then(Value::as_str).unwrap_or_default();
                        let error_description = res.get("error_description").and_then(Value::as_str).unwrap_or_default();
                        Err(MyErr::Msg(format!(
                            "登录失败！错误码：{} 错误描述:{} url：{}",
                            error, error_description, GET_TOKEN_URL
                        )))
                    }
                }
                Err(e) => Err(MyErr::Msg(format!("连接url地址失败！错误：{:?} url:{} ", e, GET_TOKEN_URL))),
            }
        }
    }
}

/// 使用 Content-Type ：application/x-www-form-urlencoded 方式 post 数据
async fn post_from_data_to_url<T: Serialize + std::fmt::Debug + ?Sized>(url: &str, form_data: &T) -> Result<Value, MyErr> {
    log::info!("\n\n\n url:{} data:{:?} \n\n\n\n", url, form_data);

    let client = reqwest::Client::new();
    let rs = client.post(url).form(form_data).send().await;
    match rs {
        Ok(res) => match res.json::<serde_json::Value>().await {
            Ok(v) => {
                log::debug!("数据读取成功！：{:?} url:{} post_form_data:{:?}", v, url, form_data);
                Ok(v)
            }
            Err(e) => {
                log::error!("读取URL数据失败！错误：{:?} url:{} post_form_data:{:?}", e, url, form_data);
                Err(MyErr::Msg("网络异常！".to_string()))
            }
        },
        Err(e) => {
            log::error!("连接url地址失败！错误：{:?} url:{} post_form_data:{:?}", e, url, form_data);
            Err(MyErr::Msg("网络异常！".to_string()))
        }
    }
}
